// Framework.cpp : Defines the entry point for the application.
//

//#include "stdafx.h"

#pragma warning(disable : 4995)

#define _CRT_SECURE_NO_DEPRECATE 1

#include "../DXUT/dxstdafx.h"
#include "Framework.h"
#include "../DXUT/MeshLoader.h"
#include "../core/crt.h"
#include "../core/crtutil.h"
#include "Shadow.h"
#include <vector>

using namespace std;

#define DEFAULT_SCENE (L"media\\planesphere.obj")


// the ray tracer object and the scene
extern CRT			g_crt;
extern TCRTScene	g_scene;
extern TCRTOutput	g_output;

extern HWND CRTDialog;
extern int	CRTDialogHeight;
extern int	CRTDialogWidth;
extern bool CRTInProgress ;
extern bool SaveCRTResult( TCHAR* filename );

INT_PTR CALLBACK CRTDialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

bool LoadScene(TCHAR *filename);
bool OnTrace();
bool OnLoadScene();
bool OnSaveImage();

//--------------------------------------------------------------------------------------
// Effect parameter handles
//--------------------------------------------------------------------------------------
D3DXHANDLE g_hAmbient = NULL;
D3DXHANDLE g_hDiffuse = NULL;
D3DXHANDLE g_hSpecular = NULL;
D3DXHANDLE g_hOpacity = NULL;
D3DXHANDLE g_hSpecularPower = NULL;
D3DXHANDLE g_hLightColor = NULL;
D3DXHANDLE g_hLightDirection = NULL;
D3DXHANDLE g_hCameraPosition = NULL;
D3DXHANDLE g_hWorld = NULL;
D3DXHANDLE g_hWorldViewProjection = NULL;


// D3D mesh used to be diaplayed in the main window
struct TD3DMesh
{
	ID3DXMesh*	mesh;
	D3DXVECTOR3 ambient;
	D3DXVECTOR3 diffuse;
	D3DXVECTOR3	specular;
	float		alpha;
	int			shiness;
};

vector<TD3DMesh> g_Meshes;

// Light representations
CDXUTDirectionWidget	g_LightControl;
D3DXVECTOR3 g_LightColor;
D3DXVECTOR3 g_LightDirection;

// Vertex declaration
D3DVERTEXELEMENT9 VERTEX_DECL[] =
{
    { 0,  0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_POSITION, 0}, 
    { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_NORMAL,   0}, 
    { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_TEXCOORD, 0},
    D3DDECL_END()
};

D3DVERTEXELEMENT9 QuadVE[] = 
{
	{0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
	{0, sizeof(D3DXVECTOR4), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
	D3DDECL_END()
};


HRESULT ConvertDXMesh(IDirect3DDevice9* pd3dDevice, TCRTMesh& mesh, TD3DMesh& result);


//--------------------------------------------------------------------------------------
// Global variables
//--------------------------------------------------------------------------------------
ID3DXFont*					g_pFont = NULL;         // Font for drawing text
ID3DXSprite*				g_pTextSprite = NULL;   // Sprite for batching draw text calls
ID3DXEffect*				g_pEffect = NULL;       // D3DX effect interface
CFirstPersonCamera			g_Camera;               // A model viewing camera
float						g_Aspect;
bool						g_bShowHelp = true;     // If true, it renders the UI control text
CDXUTDialogResourceManager	g_DialogResourceManager; // manager for shared resources of dialogs
CD3DSettingsDlg				g_SettingsDlg;          // Device settings dialog
CDXUTDialog					g_HUD;                  // dialog for standard controls


D3DXHANDLE			g_hRenderRTTTech = NULL;
D3DXHANDLE			g_hRTT	= NULL;

IDirect3DVertexDeclaration9*	g_pQuadVD = NULL;
ShadowSolution*		g_pShadowSolution = NULL;

D3DXHANDLE			g_hNoShadowTech;
D3DXHANDLE			g_hGenShadowTech;
D3DXHANDLE			g_hUseShadowTech[SSD_NUM];
D3DXHANDLE			g_hWorldToShadowMap = NULL;
D3DXHANDLE			g_hInvertDepthFunc = NULL;
D3DXHANDLE			g_hDepthBias = NULL;
D3DXHANDLE			g_hSampleOffset = NULL;

bool				g_bShowFrustum = true;
D3DXHANDLE			g_hRenderFrustumTech = NULL;
D3DXHANDLE			g_hFrustumColor = NULL;

#define DEFAULT_DEPTH_BIAS		(0.008f)
#define DEFAULT_FAR_OVER_NEAR	(4.40f)
#define DEFAULT_SHADOW_SAMPLE	(SSD_3x3)
#define DEFAULT_SHADOW_TECH		(ST_SSM)
#define DEFAULT_SHADOW_RES		(SR_1024)

//--------------------------------------------------------------------------------------
// UI control IDs
//--------------------------------------------------------------------------------------
#define IDC_STATIC              -1
#define IDC_TOGGLEFULLSCREEN    1
#define IDC_CHANGEDEVICE        2
#define IDC_RESET               3

#define IDC_LOADSCENE			4
#define IDC_SAVEIMAGE			5
#define IDC_RAYTRACE			6

// For CRT
#define IDC_SHADOW				7
#define IDC_SKYLIGHT			8

// For DX
#define IDC_SHADOWTECH			10		// To choose from ShadowTech
#define IDC_SHADOWRES			11
#define IDC_SHADOWSAMPLE		12
#define IDC_DEPTHBIAS			13
#define IDC_DEPTHBIAS_STATIC	14
#define IDC_FAROVERNEAR			15		// For LSPSM only
#define IDC_FAROVERNEAR_STATIC	16





//--------------------------------------------------------------------------------------
// Forward declarations 
//--------------------------------------------------------------------------------------
bool    CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext );
bool    CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps, void* pUserContext );
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
void    CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
void    CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext );
void    CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext );
void    CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext );
void    CALLBACK OnLostDevice( void* pUserContext );
void    CALLBACK OnDestroyDevice( void* pUserContext );

void    InitApp();
void    RenderText();
void	RenderMesh( TD3DMesh* pMesh, D3DXHANDLE hTech );
void	RenderRTT(IDirect3DDevice9* pd3dDevice);
void	RenderDebugFrustums(IDirect3DDevice9* pd3dDevice, const D3DXMATRIXA16& ViewProjMatrix);	// For debug use only
void	RenderDebugPoints(IDirect3DDevice9* pd3dDevice);
void    ResetOptions();
void	ConvertScene();

LRESULT MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);



//--------------------------------------------------------------------------------------
// Entry point to the program. Initializes everything and goes into a message processing 
// loop. Idle time is used to render the scene.
//--------------------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
	// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

    // Set the callback functions. These functions allow DXUT to notify
    // the application about device changes, user input, and windows messages.  The 
    // callbacks are optional so you need only set callbacks for events you're interested 
    // in. However, if you don't handle the device reset/lost callbacks then the sample 
    // framework won't be able to reset your device since the application must first 
    // release all device resources before resetting.  Likewise, if you don't handle the 
    // device created/destroyed callbacks then DXUT won't be able to 
    // recreate your device resources.
    DXUTSetCallbackDeviceCreated( OnCreateDevice );
    DXUTSetCallbackDeviceReset( OnResetDevice );
    DXUTSetCallbackDeviceLost( OnLostDevice );
    DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackKeyboard( KeyboardProc );
    DXUTSetCallbackFrameRender( OnFrameRender );
    DXUTSetCallbackFrameMove( OnFrameMove );

    // Although multisampling is supported for render target surfaces, those surfaces
    // exist without a parent texture, and must therefore be copied to a texture 
    // surface if they're to be used as a source texture. This sample relies upon 
    // several render targets being used as source textures, and therefore it makes
    // sense to disable multisampling altogether.
    CGrowableArray<D3DMULTISAMPLE_TYPE>* pMultiSampleTypeList = DXUTGetEnumeration()->GetPossibleMultisampleTypeList();
    pMultiSampleTypeList->RemoveAll();
    pMultiSampleTypeList->Add( D3DMULTISAMPLE_NONE );
    DXUTGetEnumeration()->SetMultisampleQualityMax( 0 );

    // Show the cursor and clip it when in full screen
    DXUTSetCursorSettings( true, true );

    InitApp();

    // Initialize DXUT and create the desired Win32 window and Direct3D 
    // device for the application. Calling each of these functions is optional, but they
    // allow you to set several options which control the behavior of the framework.
    DXUTInit( true, true, true ); // Parse the command line, handle the default hotkeys, and show msgboxes
    DXUTCreateWindow( L"CRT" );
    DXUTCreateDevice( D3DADAPTER_DEFAULT, true, 1024, 768, IsDeviceAcceptable, ModifyDeviceSettings );

	CRTDialog = CreateDialog( DXUTGetHINSTANCE(), MAKEINTRESOURCE(IDD_RAYTRACE), DXUTGetHWND(), CRTDialogProc);

	// Pass control to DXUT for handling the message pump and 
    // dispatching render calls. DXUT will call your FrameMove 
    // and FrameRender callback when there is idle time between handling window messages.
    DXUTMainLoop();

    // Perform any application-level cleanup here. Direct3D device resources are released within the
    // appropriate callback functions and therefore don't require any cleanup code here.

    return DXUTGetExitCode();
}


//--------------------------------------------------------------------------------------
// Initialize the app 
//--------------------------------------------------------------------------------------
void InitApp()
{
	//TCHAR	msg[128];

    // Initialize dialogs
    g_SettingsDlg.Init( &g_DialogResourceManager );
    g_HUD.Init( &g_DialogResourceManager );

    g_HUD.SetCallback( OnGUIEvent ); int iY = 10; 
    g_HUD.AddButton( IDC_TOGGLEFULLSCREEN, L"Toggle full screen (F)", 35, iY, 125, 22, L'F' );
    //g_HUD.AddButton( IDC_TOGGLEREF, L"Toggle REF (F3)", 35, iY += 24, 125, 22, VK_F3 );
    g_HUD.AddButton( IDC_CHANGEDEVICE, L"Change device (F2)", 35, iY += 24, 125, 22, VK_F2 );
    g_HUD.AddButton( IDC_RESET, L"Reset Options (R)", 35, iY += 24, 125, 22, L'R' );
    iY += 24;
	g_HUD.AddButton( IDC_LOADSCENE, L"Load scene (L)", 35, iY += 24, 125, 22, L'L' );
	g_HUD.AddButton( IDC_SAVEIMAGE, L"Save image (P)", 35, iY += 24, 125, 22, L'P' );
	g_HUD.AddButton( IDC_RAYTRACE, L"Ray trace (T)", 35, iY += 24, 125, 22, L'R' );
	iY += 24;
	//g_HUD.AddComboBox( IDC_SHADOWTECH, -50, iY += 24, 200, 22);
	//g_HUD.AddComboBox( IDC_SHADOWRES, -50, iY += 24, 200, 22);
	//g_HUD.AddComboBox( IDC_SHADOWSAMPLE, -50, iY += 24, 200, 22);
	//g_HUD.AddSlider( IDC_DEPTHBIAS, -50, iY += 24, 200, 22, 0, 500, int(DEFAULT_DEPTH_BIAS * 10000.0f));
	//_sntprintf( msg, 128, TEXT("DepthBias: %.3f"), DEFAULT_DEPTH_BIAS ); msg[127] = 0;
	//g_HUD.AddStatic( IDC_DEPTHBIAS_STATIC, msg, 0, iY += 12, 100, 22 );
	//g_HUD.AddSlider( IDC_FAROVERNEAR, -50, iY += 24, 200, 22, 101, 500, int(DEFAULT_FAR_OVER_NEAR * 100.0f));
	//_sntprintf( msg, 128, TEXT("FarOverNear: %.3f"), DEFAULT_FAR_OVER_NEAR ); msg[127] = 0;
	//g_HUD.AddStatic( IDC_FAROVERNEAR_STATIC, msg, 0, iY += 12, 100, 22 );
	//iY += 24;
	g_HUD.AddCheckBox( IDC_SHADOW, L"Shadow", 35, iY += 24, 125, 22, true);

	iY += 24;
	g_HUD.AddCheckBox( IDC_SKYLIGHT, L"Skylight", 35, iY += 24, 125, 22, false);
	
	//CDXUTComboBox* CB = g_HUD.GetComboBox( IDC_SHADOWTECH );
	//for (int i = 0; i < ST_NUM; i++)
	//	CB->AddItem( ShadowTechName[i], 0 );
	//CB->SetSelectedByIndex( DEFAULT_SHADOW_TECH );

	//CB = g_HUD.GetComboBox( IDC_SHADOWRES );
	//for (int i = 0; i < SR_NUM; i++)
	//	CB->AddItem( ShadowResName[i], 0 );
	//CB->SetSelectedByIndex( DEFAULT_SHADOW_RES );

	//CB = g_HUD.GetComboBox( IDC_SHADOWSAMPLE );
	//for (int i = 0; i < SSD_NUM; i++)
	//	CB->AddItem( ShadowSampleName[i], 0 );
	//CB->SetSelectedByIndex( DEFAULT_SHADOW_SAMPLE );

	ResetOptions();

	CRTUtilInit();
}


//-----------------------------------------------------------------------------
// Name: ResetOptions()
// Desc: Reset all user-controlled options to default values
//-----------------------------------------------------------------------------
void ResetOptions()
{
	TCHAR	msg[128];

	if (g_pShadowSolution)
	{
		g_pShadowSolution->SetDepthBias( DEFAULT_DEPTH_BIAS );
		g_pShadowSolution->SetFarOverNear( DEFAULT_FAR_OVER_NEAR );

		g_HUD.GetSlider( IDC_DEPTHBIAS )->SetValue( int(g_pShadowSolution->GetDepthBias() * 10000.0f) );
		g_HUD.GetSlider( IDC_FAROVERNEAR )->SetValue( int(g_pShadowSolution->GetFarOverNear() * 100.0f) );

		_sntprintf( msg, 128, TEXT("DepthBias: %.3f"), DEFAULT_DEPTH_BIAS ); msg[127] = 0;
		g_HUD.GetStatic( IDC_DEPTHBIAS_STATIC )->SetText( msg );
		_sntprintf( msg, 128, TEXT("FarOverNear: %.3f"), DEFAULT_FAR_OVER_NEAR ); msg[127] = 0;
		g_HUD.GetStatic( IDC_FAROVERNEAR_STATIC )->SetText( msg );
	}
}


//--------------------------------------------------------------------------------------
// Called during device initialization, this code checks the device for some 
// minimum set of capabilities, and rejects those that don't pass by returning false.
//--------------------------------------------------------------------------------------
bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, 
                                  D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext )
{
    // Skip backbuffer formats that don't support alpha blending
    IDirect3D9* pD3D = DXUTGetD3DObject(); 
    if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
                    AdapterFormat, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, 
                    D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
        return false;

    if(pCaps->PixelShaderVersion < D3DPS_VERSION(2,0))
        return false;

    // No fallback yet, so need to support D3DFMT_A16B16G16R16F render target
    if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
                    AdapterFormat, D3DUSAGE_RENDERTARGET, 
                    D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F) ) )
    {
        return false;
    }

    // No fallback yet, so need to support D3DFMT_R32F or D3DFMT_R16F render target
    if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
                    AdapterFormat, D3DUSAGE_RENDERTARGET, 
                    D3DRTYPE_TEXTURE, D3DFMT_R32F) ) )
    {
        if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
                        AdapterFormat, D3DUSAGE_RENDERTARGET, 
                        D3DRTYPE_TEXTURE, D3DFMT_R16F) ) )
            return false;
    }

    // Need to support post-pixel processing
    if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
        AdapterFormat, D3DUSAGE_RENDERTARGET | D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, 
        D3DRTYPE_SURFACE, BackBufferFormat ) ) )
    {
        return false;
    }


    return true;
}


//--------------------------------------------------------------------------------------
// This callback function is called immediately before a device is created to allow the 
// application to modify the device settings. The supplied pDeviceSettings parameter 
// contains the settings that the framework has selected for the new device, and the 
// application can make any desired changes directly to this structure.  Note however that 
// DXUT will not correct invalid device settings so care must be taken 
// to return valid device settings, otherwise IDirect3D9::CreateDevice() will fail.  
//--------------------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps, void* pUserContext )
{
    // If device doesn't support HW T&L or doesn't support 2.0 vertex shaders in HW 
    // then switch to SWVP.
    if( (pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0 ||
         pCaps->VertexShaderVersion < D3DVS_VERSION(2,0) )
    {
        pDeviceSettings->BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
    }

    // Debugging vertex shaders requires either REF or software vertex processing 
    // and debugging pixel shaders requires REF.  
#ifdef DEBUG_VS
    if( pDeviceSettings->DeviceType != D3DDEVTYPE_REF )
    {
        pDeviceSettings->BehaviorFlags &= ~D3DCREATE_HARDWARE_VERTEXPROCESSING;
        pDeviceSettings->BehaviorFlags &= ~D3DCREATE_PUREDEVICE;                            
        pDeviceSettings->BehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
    }
#endif
#ifdef DEBUG_PS
    pDeviceSettings->DeviceType = D3DDEVTYPE_REF;
#endif

    // For the first device created if its a REF device, optionally display a warning dialog box
    static bool s_bFirstTime = true;
    if( s_bFirstTime )
    {
        s_bFirstTime = false;
		pDeviceSettings->pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;		// No vert sync
        if( pDeviceSettings->DeviceType == D3DDEVTYPE_REF )
            DXUTDisplaySwitchingToREFWarning();
    }

    return true;
}


//--------------------------------------------------------------------------------------
// This callback function will be called immediately after the Direct3D device has been 
// created, which will happen during application initialization and windowed/full screen 
// toggles. This is the best location to create D3DPOOL_MANAGED resources since these 
// resources need to be reloaded whenever the device is destroyed. Resources created  
// here should be released in the OnDestroyDevice callback. 
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
{
    HRESULT hr;
	WCHAR str[MAX_PATH];

    V_RETURN( g_DialogResourceManager.OnCreateDevice( pd3dDevice ) );
    V_RETURN( g_SettingsDlg.OnCreateDevice( pd3dDevice ) );
    // Determine which of D3DFMT_R16F or D3DFMT_R32F to use for luminance texture
    D3DCAPS9 Caps;
    IDirect3D9* pD3D = DXUTGetD3DObject();
    if( !pD3D ) 
        return E_FAIL;
    D3DDISPLAYMODE DisplayMode;
    pd3dDevice->GetDeviceCaps( &Caps );
    pd3dDevice->GetDisplayMode( 0, &DisplayMode );

    // Define DEBUG_VS and/or DEBUG_PS to debug vertex and/or pixel shaders with the 
    // shader debugger. Debugging vertex shaders requires either REF or software vertex 
    // processing, and debugging pixel shaders requires REF.  The 
    // D3DXSHADER_FORCE_*_SOFTWARE_NOOPT flag improves the debug experience in the 
    // shader debugger.  It enables source level debugging, prevents instruction 
    // reordering, prevents dead code elimination, and forces the compiler to compile 
    // against the next higher available software target, which ensures that the 
    // unoptimized shaders do not exceed the shader model limitations.  Setting these 
    // flags will cause slower rendering since the shaders will be unoptimized and 
    // forced into software.  See the DirectX documentation for more information about 
    // using the shader debugger.
    DWORD dwShaderFlags = D3DXFX_NOT_CLONEABLE;

    #if defined( DEBUG ) || defined( _DEBUG )
    // Set the D3DXSHADER_DEBUG flag to embed debug information in the shaders.
    // Setting this flag improves the shader debugging experience, but still allows 
    // the shaders to be optimized and to run exactly the way they will run in 
    // the release configuration of this program.
    dwShaderFlags |= D3DXSHADER_DEBUG;
    #endif

    #ifdef DEBUG_VS
        dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT;
    #endif
    #ifdef DEBUG_PS
        dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT;
    #endif

    // Read the D3DX effect file
    V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"DXshader.fx" ) );

	// Initialize the shadow map resources
	g_pShadowSolution = new ShadowSolution( pd3dDevice );
	g_pShadowSolution->SetFarOverNear( DEFAULT_FAR_OVER_NEAR );
	g_pShadowSolution->SetDepthBias( DEFAULT_DEPTH_BIAS );
	g_pShadowSolution->SetSample( DEFAULT_SHADOW_SAMPLE );
	g_pShadowSolution->SetTech( DEFAULT_SHADOW_TECH );
	g_pShadowSolution->SetResolution( DEFAULT_SHADOW_RES );

	// If this fails, there should be debug output as to 
    // they the .fx file failed to compile
    V_RETURN( D3DXCreateEffectFromFile( pd3dDevice, str, NULL, NULL, dwShaderFlags, 
                                        NULL, &g_pEffect, NULL ) );
	
    // Cache the effect handles
    g_hAmbient = g_pEffect->GetParameterBySemantic( 0, "Ambient" );
    g_hDiffuse = g_pEffect->GetParameterBySemantic( 0, "Diffuse" );
    g_hSpecular = g_pEffect->GetParameterBySemantic( 0, "Specular" );
    g_hOpacity = g_pEffect->GetParameterBySemantic( 0, "Opacity" );
    g_hSpecularPower = g_pEffect->GetParameterBySemantic( 0, "SpecularPower" );
    g_hLightColor = g_pEffect->GetParameterBySemantic( 0, "LightColor" );
    g_hLightDirection = g_pEffect->GetParameterBySemantic( 0, "LightDirection" );
    g_hCameraPosition = g_pEffect->GetParameterBySemantic( 0, "CameraPosition" );
    g_hWorld = g_pEffect->GetParameterBySemantic( 0, "World" );
    g_hWorldViewProjection = g_pEffect->GetParameterBySemantic( 0, "WorldViewProjection" );
	g_hRTT = g_pEffect->GetParameterBySemantic( 0, "Texture" );
	g_hWorldToShadowMap = g_pEffect->GetParameterBySemantic( 0, "WorldToShadowMap" );
	g_hInvertDepthFunc = g_pEffect->GetParameterByName( 0, "g_bInvertDepthFunc" );
	g_hDepthBias = g_pEffect->GetParameterByName( 0, "g_fDepthBias" );
	g_hSampleOffset = g_pEffect->GetParameterByName( 0, "g_vSampleOffset" );
	g_hFrustumColor = g_pEffect->GetParameterByName( 0, "g_vFrustumColor" );

	g_hNoShadowTech = g_pEffect->GetTechniqueByName( "NoShadow" );
	g_hUseShadowTech[SSD_1x1] = g_pEffect->GetTechniqueByName( "UseShadow_1x1" );
	g_hUseShadowTech[SSD_2x2] = g_pEffect->GetTechniqueByName( "UseShadow_2x2" );
	g_hUseShadowTech[SSD_3x3] = g_pEffect->GetTechniqueByName( "UseShadow_3x3" );
	g_hUseShadowTech[SSD_4x4] = g_pEffect->GetTechniqueByName( "UseShadow_4x4" );
	g_hUseShadowTech[SSD_5x5] = g_pEffect->GetTechniqueByName( "UseShadow_5x5" );
	g_hUseShadowTech[SSD_6x6] = g_pEffect->GetTechniqueByName( "UseShadow_6x6" );
	g_hUseShadowTech[SSD_7x7] = g_pEffect->GetTechniqueByName( "UseShadow_7x7" );
	g_hUseShadowTech[SSD_8x8] = g_pEffect->GetTechniqueByName( "UseShadow_8x8" );
	g_hGenShadowTech = g_pEffect->GetTechniqueByName( "GenShadow" );
	g_hRenderRTTTech = g_pEffect->GetTechniqueByName( "RenderRTT" );
	g_hRenderFrustumTech = g_pEffect->GetTechniqueByName( "RenderFrustum" );

	// Load the default scene
	DXUTFindDXSDKMediaFileCch(str, MAX_PATH, DEFAULT_SCENE);
	LoadScene( str );

	// Initialize the font
    V_RETURN( D3DXCreateFont( pd3dDevice, 15, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, 
                         OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 
                         L"Arial", &g_pFont ) );

	// Initialize the light source stuff
	V_RETURN( CDXUTDirectionWidget::StaticOnCreateDevice( pd3dDevice ) );

	V( pd3dDevice->CreateVertexDeclaration( QuadVE, &g_pQuadVD ) );

	return S_OK;
}



//--------------------------------------------------------------------------------------
// This callback function will be called once at the beginning of every frame. This is the
// best location for your application to handle updates to the scene, but is not 
// intended to contain actual rendering calls, which should instead be placed in the 
// OnFrameRender callback.  
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
    // Update the camera's position based on user input 
    g_Camera.FrameMove( fElapsedTime );
}


//--------------------------------------------------------------------------------------
// This callback function will be called at the end of every frame to perform all the 
// rendering calls for the scene, and it will also be called if the window needs to be 
// repainted. After this function has returned, DXUT will call 
// IDirect3DDevice9::Present to display the contents of the next buffer in the swap chain
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
    // If the settings dialog is being shown, then
    // render it instead of rendering the app's scene
    if( g_SettingsDlg.IsActive() )
    {
        g_SettingsDlg.OnRender( fElapsedTime );
        return;
    }

    HRESULT hr;
    D3DXMATRIXA16 mWorld;
    D3DXMATRIXA16 mView;
    D3DXMATRIXA16 mProj;
    D3DXMATRIXA16 mWorldViewProjection;
	D3DXMATRIXA16 mLightViewProjection;
	D3DXMATRIXA16 mLightPPS2ShadowTex;
	D3DXMatrixIdentity( &mWorld );
    
    // Render the scene
    if( SUCCEEDED( pd3dDevice->BeginScene() ) )
    {
		// Render depth texture from light view
		g_pShadowSolution->SetCameraPosition( *g_Camera.GetEyePt() );
		g_pShadowSolution->SetCameraDirection( *g_Camera.GetWorldAhead() );
		g_pShadowSolution->SetCameraUp( *g_Camera.GetWorldUp() );
		g_pShadowSolution->SetCameraParam( g_Camera.GetNearClip(), g_Camera.GetFarClip(), g_Aspect, D3DX_PI / 4 );
		V( g_pShadowSolution->PreRenderDepth() );
		V( g_pEffect->SetMatrix( g_hWorldViewProjection, &g_pShadowSolution->GetShadowGenMatrix() ) );
		for (vector<TD3DMesh>::iterator it = g_Meshes.begin(); it != g_Meshes.end(); it++)
		{
			RenderMesh( &(*it), g_hGenShadowTech );
		}
		RenderDebugPoints(pd3dDevice);
		V( g_pShadowSolution->PostRenderDepth() );

		// Clear the render target and the zbuffer 
		V( pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0) );

		// Get the projection & view matrix from the camera class        
        mView = *g_Camera.GetViewMatrix();
        mProj = *g_Camera.GetProjMatrix();       
        
        mWorldViewProjection = /*mWorld * */mView * mProj;

        // Update the effect's variables. 
		V( g_pEffect->SetMatrix( g_hWorldToShadowMap, &g_pShadowSolution->GetShadowUseMatrix() ) );
        V( g_pEffect->SetMatrix( g_hWorldViewProjection, &mWorldViewProjection ) );
        V( g_pEffect->SetMatrix( g_hWorld, &mWorld ) );
		V( g_pEffect->SetTexture( g_hRTT, g_pShadowSolution->GetDepthTex() ))
        V( g_pEffect->SetValue( g_hCameraPosition, g_Camera.GetEyePt(), sizeof(D3DXVECTOR3) ) );
		V( g_pEffect->SetValue( g_hLightDirection, g_LightDirection, sizeof(D3DXVECTOR3) ) );
		V( g_pEffect->SetValue( g_hLightColor, g_LightColor, sizeof(D3DXVECTOR3) ) );
		V( g_pEffect->SetBool( g_hInvertDepthFunc, g_pShadowSolution->ShouldInvertDepthFunc() ) );
		V( g_pEffect->SetFloat( g_hDepthBias, g_pShadowSolution->GetDepthBias() ) );
		V( g_pEffect->SetVectorArray( g_hSampleOffset, g_pShadowSolution->GetSampleOffset(), g_pShadowSolution->GetSampleNum() ) );
		for (vector<TD3DMesh>::iterator it = g_Meshes.begin(); it != g_Meshes.end(); it++)
		{
			RenderMesh( &(*it), g_pShadowSolution->GetTech() == ST_NO ? g_hNoShadowTech : g_hUseShadowTech[g_pShadowSolution->GetSample()] );
		}

		// Render the render target(s)
		//RenderRTT(pd3dDevice);

		RenderDebugFrustums(pd3dDevice, mWorldViewProjection);

		// Render the light source using DXUT library
		D3DXCOLOR ArrowColor(g_LightColor.x, g_LightColor.y, g_LightColor.z, 255);
		V( g_LightControl.OnRender( ArrowColor, &mView, &mProj, g_Camera.GetEyePt() ) );


        RenderText();
        V( g_HUD.OnRender( fElapsedTime ) );

        V( pd3dDevice->EndScene() );
    }
}

struct TLVertex
{
	D3DXVECTOR4 p;
	D3DXVECTOR2 t;
};

void RenderDebugPoints(IDirect3DDevice9* pd3dDevice)
{
	D3DXVECTOR4 vPointColor(1, 0, 0, 1);
	UINT NumPass;
	HRESULT hr;

	TLVertex pointv[16];
	int nPointV = min(16, g_pShadowSolution->GetDebugVertexNum());

	for (int i = 0; i < nPointV; i++)
	{
		const VEC3* SrcP = g_pShadowSolution->GetDebugVertex(i);
		pointv[i].p = D3DXVECTOR4(SrcP->x, SrcP->y, SrcP->z, 1.0f);
		pointv[i].t = D3DXVECTOR2(0,0);
	}

	V( pd3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE ) );
	V( pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, D3DZB_FALSE ) );
	float PointSize = 32.0f;
	V( pd3dDevice->SetRenderState(D3DRS_POINTSIZE, *(DWORD*)&PointSize) );

	V( g_pEffect->SetTechnique( g_hRenderFrustumTech ) );	
	V( pd3dDevice->SetVertexDeclaration( g_pQuadVD ) );
	V( g_pEffect->SetVector( g_hFrustumColor, &vPointColor ) );
	V( g_pEffect->Begin( &NumPass, 0 ) );
	if (NumPass != 1)
		return;
	V( g_pEffect->BeginPass( 0 ) );
	V( pd3dDevice->DrawPrimitiveUP( D3DPT_POINTLIST, nPointV, pointv, sizeof( TLVertex )));
	V( g_pEffect->EndPass() );
	V( g_pEffect->End() );

	V( pd3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE ) );
	V( pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, D3DZB_TRUE ) );
}

void RenderDebugFrustums(IDirect3DDevice9* pd3dDevice, const D3DXMATRIXA16& ViewProjMatrix)	// For debug use only
{
	static D3DXVECTOR4 vColor[4] = 
	{
		D3DXVECTOR4(1, 0, 0, 1),
		D3DXVECTOR4(1, 1, 0, 1),
		D3DXVECTOR4(0, 1, 0, 1),
		D3DXVECTOR4(1, 0, 1, 1)
	};

	if (g_bShowFrustum && g_pShadowSolution->GetDebugFrustumNum())
	{
		HRESULT			hr;
		UINT			NumPass;

		TLVertex Vertex[8];


		V( pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME ) );

		V( g_pEffect->SetTechnique( g_hRenderFrustumTech ) );
		V( g_pEffect->SetMatrix( g_hWorldViewProjection, &ViewProjMatrix ) );		
		
		pd3dDevice->SetVertexDeclaration( g_pQuadVD );
		
		V( g_pEffect->Begin( &NumPass, 0 ) );

		if (NumPass != 1)
			return;

		V( g_pEffect->BeginPass( 0 ) );

		for (int i = 0; i < g_pShadowSolution->GetDebugFrustumNum(); i++)
		{
			V( g_pEffect->SetVector( g_hFrustumColor, (vColor + i % 4) ) );
			V( g_pEffect->CommitChanges() );

			const Frustum* Fru = g_pShadowSolution->GetDebugFrustum(i);
			if (Fru == NULL)
				continue;

			for (int j = 0; j < 8; j++)
			{
				Vertex[j].p = D3DXVECTOR4(Fru->vert[j].x, Fru->vert[j].y, Fru->vert[j].z, 1.0f);
			}

			pd3dDevice->DrawIndexedPrimitiveUP( D3DPT_LINELIST,
				0, 8, 
				12, 
				Frustum::line_list,
				D3DFMT_INDEX32 ,
				Vertex, 
				sizeof( TLVertex )
				);
		}

		// Render the lines
		int LineList[16 * 2];
		TLVertex LineVertex[17];		
		int nLine = min(16, g_pShadowSolution->GetDebugVertexNum());
		if (nLine)
		{
			D3DXVECTOR4 LineColor(0, 1, 1, 1);
			VEC3 CenterPoint(0,0,0);

			for (int i = 0; i < nLine; i++)
			{
				const VEC3* P = g_pShadowSolution->GetDebugVertex(i);

				CenterPoint += *P;
				LineList[i*2] = 0;
				LineList[i*2+1] = i+1;
				LineVertex[i+1].p = D3DXVECTOR4(P->x, P->y, P->z, 1.0f);
			}

			CenterPoint *= 1.0f / nLine;
			LineVertex[0].p = D3DXVECTOR4(CenterPoint.x, CenterPoint.y, CenterPoint.z, 1.0f);

			V( g_pEffect->SetVector( g_hFrustumColor, &LineColor ) );
			V( g_pEffect->CommitChanges() );

			pd3dDevice->DrawIndexedPrimitiveUP( D3DPT_LINELIST,
				0, nLine + 1, 
				nLine, 
				LineList,
				D3DFMT_INDEX32 ,
				LineVertex, 
				sizeof( TLVertex )
				);
		}

		V( g_pEffect->EndPass() );

		V( g_pEffect->End() );

		V( pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ) );
	}
}

//--------------------------------------------------------------------------------------
void RenderRTT(IDirect3DDevice9* pd3dDevice)
{
	HRESULT			hr;
	UINT			NumPass;
	D3DXMATRIXA16	mWorld;
	D3DXMATRIXA16	mView;
	D3DXMATRIXA16	mProj;
	D3DXMATRIXA16	mWorldViewProj;

	D3DXVECTOR4 vLineColor = D3DXVECTOR4(0, 0, 1, 1);

	TLVertex v[4];
	TLVertex linev[4];

	float	StartX = -1.0f;
	float	StartY = 0.6f;
	float	Width  = 0.4f;
	float	Height  = 0.4f;

	v[0].p = D3DXVECTOR4( StartX, StartY, 0.0f, 1.0f );
	v[0].t = D3DXVECTOR2( 0.0f, 1.0f );

	v[1].p = D3DXVECTOR4( StartX + Width, StartY, 0.0f, 1.0f );
	v[1].t = D3DXVECTOR2( 1.0f, 1.0f );

	v[2].p = D3DXVECTOR4( StartX, StartY + Height, 0.0f, 1.0f );
	v[2].t = D3DXVECTOR2( 0.0f, 0.0f );

	v[3].p = D3DXVECTOR4( StartX + Width, StartY + Height, 0.0f, 1.0f );
	v[3].t = D3DXVECTOR2( 1.0f, 0.0f );

	linev[0] = v[0];
	linev[1] = v[1];
	linev[2] = v[1];
	linev[3] = v[3];

	D3DXMatrixIdentity( &mWorld );
	D3DXMatrixIdentity( &mView );
	D3DXMatrixOrthoRH( &mProj, 2.0f, 2.0f, -1.0f, 1.0f );
	mWorldViewProj = mWorld * mView * mProj;

	V( pd3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE ) );
	V( pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, D3DZB_FALSE ) );
	V( pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE));
	V( pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA));
	V( pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA));

	V( g_pEffect->SetMatrix( g_hWorldViewProjection, &mWorldViewProj ) );
	V( g_pEffect->SetMatrix( g_hWorld, &mWorld ) );
	V( g_pEffect->SetTexture( g_hRTT, g_pShadowSolution->GetDepthTex() ) );

	V( g_pEffect->SetTechnique( g_hRenderRTTTech ) );
	V( g_pEffect->Begin( &NumPass, 0 ) );	
	if (NumPass != 1)
		return;
	V( g_pEffect->BeginPass( 0 ) );
	V( pd3dDevice->SetVertexDeclaration( g_pQuadVD ) );
	V( pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, v, sizeof( TLVertex ) ) );
	V( g_pEffect->EndPass() );
	V( g_pEffect->End() );


	// Render the rectangle edge
	V( g_pEffect->SetTechnique( g_hRenderFrustumTech ) );	
	V( pd3dDevice->SetVertexDeclaration( g_pQuadVD ) );
	V( g_pEffect->SetVector( g_hFrustumColor, &vLineColor ) );
	V( g_pEffect->Begin( &NumPass, 0 ) );
	if (NumPass != 1)
		return;
	V( g_pEffect->BeginPass( 0 ) );
	V( pd3dDevice->DrawPrimitiveUP( D3DPT_LINELIST, 2, linev, sizeof( TLVertex ) ) );

	V( g_pEffect->EndPass() );
	V( g_pEffect->End() );

	V( pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE));
	V( pd3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE ) );
	V( pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, D3DZB_TRUE ) );
}

//--------------------------------------------------------------------------------------
void RenderMesh( TD3DMesh* pMesh, D3DXHANDLE hTech )
{
	HRESULT hr;
	UINT iPass, cPasses;

	if (pMesh == NULL)
		return;

	V( g_pEffect->SetValue( g_hAmbient, pMesh->ambient, sizeof(D3DXVECTOR3) ) );
	V( g_pEffect->SetValue( g_hDiffuse, pMesh->diffuse, sizeof(D3DXVECTOR3) ) );
	V( g_pEffect->SetValue( g_hSpecular, pMesh->specular, sizeof(D3DXVECTOR3) ) );
	V( g_pEffect->SetFloat( g_hOpacity, pMesh->alpha ) );
	V( g_pEffect->SetInt( g_hSpecularPower, pMesh->shiness ) );

	V( g_pEffect->SetTechnique( hTech ) );
	V( g_pEffect->Begin(&cPasses, 0) );

	for (iPass = 0; iPass < cPasses; iPass++)
	{
		V( g_pEffect->BeginPass(iPass) );
		V( pMesh->mesh->DrawSubset( 0 ) );
		V( g_pEffect->EndPass() );
	}
	V( g_pEffect->End() );
}

//--------------------------------------------------------------------------------------
// Render the help and statistics text. This function uses the ID3DXFont interface for 
// efficient text rendering.
//--------------------------------------------------------------------------------------
void RenderText()
{
    // The helper object simply helps keep track of text position, and color
    // and then it calls pFont->DrawText( g_pSprite, strMsg, -1, &rc, DT_NOCLIP, g_clr );
    // If NULL is passed in as the sprite object, then it will work however the 
    // pFont->DrawText() will not be batched together.  Batching calls will improves performance.
    CDXUTTextHelper txtHelper( g_pFont, g_pTextSprite, 15 );

    // Output statistics
    txtHelper.Begin();

    txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );

    // Draw help
    if( g_bShowHelp )
    {
        const D3DSURFACE_DESC* pd3dsdBackBuffer = DXUTGetBackBufferSurfaceDesc();

		txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 0.25f, 0.0f, 1.0f ) );

		txtHelper.SetInsertionPos( 40, pd3dsdBackBuffer->Height-15*8 );
		txtHelper.DrawTextLine( DXUTGetFrameStats(true) );

		txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 0.75f, 0.0f, 1.0f ) );

		txtHelper.SetInsertionPos( 10, pd3dsdBackBuffer->Height-15*6 );

#if defined(DEBUG) || defined(_DEBUG)
        txtHelper.DrawTextLine( L"DEBUG Stats (F1 to hide):" );
#else
		txtHelper.DrawTextLine( L"RELEASE Stats (F1 to hide):" );
#endif

        txtHelper.SetInsertionPos( 40, pd3dsdBackBuffer->Height-15*5 );

		TCRTOutput output = g_crt.GetOutput();

		txtHelper.DrawFormattedTextLine( L"%d mesh, %d light, %d vertex, %d triangle	\
			\n\t %.2fs@%d*%d image, %.2fs ray-triangle time\
			\n\t %d ray, %d ray-aabb, %d ray-tri, %.2f aabb/ray, %.2f triangle/ray", 
			g_scene.GetMeshCount(),
			g_scene.GetLightCount(),
			g_scene.GetVertexCount(),
			g_scene.GetTriangleCount(),
			output.timeused,
			output.imagew,
			output.imageh, 
			output.raytritime, 
			output.nray, 
			output.nrayaabb, 
			output.nraytriangle,
			float(output.nrayaabb) / max(output.nray, 1),
			float(output.nraytriangle) / max(output.nray, 1)
			);
    }
    else
    {
        txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
        txtHelper.DrawTextLine( L"Press F1 for stats" );
    }
    txtHelper.End();
}


//--------------------------------------------------------------------------------------
// Before handling window messages, DXUT passes incoming windows 
// messages to the application through this callback function. If the application sets 
// *pbNoFurtherProcessing to TRUE, then DXUT will not process this message.
//--------------------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext )
{
    // Always allow dialog resource manager calls to handle global messages
    // so GUI state is updated correctly
    *pbNoFurtherProcessing = g_DialogResourceManager.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;

    if( g_SettingsDlg.IsActive() )
    {
        g_SettingsDlg.MsgProc( hWnd, uMsg, wParam, lParam );
        return 0;
    }

    // Give the dialogs a chance to handle the message first
    *pbNoFurtherProcessing = g_HUD.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;
    //*pbNoFurtherProcessing = g_SampleUI.MsgProc( hWnd, uMsg, wParam, lParam );
    //if( *pbNoFurtherProcessing )
    //    return 0;

	if (g_LightControl.HandleMessages( hWnd, uMsg, wParam, lParam ))
	{
		g_LightDirection = -g_LightControl.GetLightDirection();
		g_pShadowSolution->SetLightDirection( g_LightDirection );

		g_scene.lights[0].direction = g_LightDirection;
	}


    // Pass all remaining windows messages to camera so it can respond to user input
    g_Camera.HandleMessages( hWnd, uMsg, wParam, lParam );

    return 0;
}


//--------------------------------------------------------------------------------------
// As a convenience, DXUT inspects the incoming windows messages for
// keystroke messages and decodes the message parameters to pass relevant keyboard
// messages to the application.  The framework does not remove the underlying keystroke 
// messages, which are still passed to the application's MsgProc callback.
//--------------------------------------------------------------------------------------
void CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext )
{
    if( bKeyDown )
    {
        switch( nChar )
        {
            case VK_F1: g_bShowHelp = !g_bShowHelp; break;

			case VK_F5 : g_bShowFrustum = !g_bShowFrustum; break;

			case VK_F6 : if (g_pShadowSolution)
							 g_pShadowSolution->CreateDebugFrustums();
				break;
        }
    }
}


extern void RayTraceScene();

bool OnTrace()
{
	RECT	rect;
	ShowWindow( CRTDialog, SW_SHOW );

	static bool FirstShowUp = TRUE;

	if (FirstShowUp)
	{
		FirstShowUp = FALSE; 
		// Move the dialog so that it fits the lower-right corner of the main window
		GetWindowRect( DXUTGetHWND(), &rect );

		MoveWindow( CRTDialog, 
			rect.right - CRTDialogWidth, 
			rect.bottom - CRTDialogHeight,
			CRTDialogWidth,
			CRTDialogHeight, 
			TRUE);
	}

	RayTraceScene();
	//PostMessage( CRTDialog, WM_PAINT, 0, 0 );

	return true;
}

bool LoadScene(TCHAR *filename)
{
	if (!g_scene.LoadSceneFromOBJ( filename ))
		return false;

	if (!g_scene.Optimize())
	{
		bool test = g_scene.Optimize();
		return false;
	}
	ConvertScene();

    // Initialize the camera
	VEC3 origin = g_scene.aabb.GetCenter();
	VEC3 size = g_scene.aabb.GetSize();
	VEC3 from = origin + VEC3(0,0,-3) * size;
	//from.y = origin.y; 
	VEC3 maxb = origin + size * 80.0f;
	VEC3 minb = origin - size * 80.0f;
	float scale = size.Length() * 2.0f;

	D3DXVECTOR3   vFromPt(from.x, from.y, from.z);
    D3DXVECTOR3   vLookatPt(origin.x, origin.y, origin.z);
    g_Camera.SetViewParams( &vFromPt, &vLookatPt );

	float maxEdge = max(size.x, size.y);
	maxEdge = max(maxEdge, size.z);
    // Set options for the first-person camera
    g_Camera.SetScalers( 0.004f, maxEdge * 1.0f );
    g_Camera.SetDrag( true );
    g_Camera.SetEnableYAxisMovement( true );

    D3DXVECTOR3 vMin = D3DXVECTOR3( minb.x, minb.y, minb.z );
    D3DXVECTOR3 vMax = D3DXVECTOR3( maxb.x, maxb.y, maxb.z );
    g_Camera.SetClipToBoundary( TRUE, &vMin, &vMax ); 

    // Setup the camera's projection parameters
    g_Camera.SetProjParams( D3DX_PI/4, g_Aspect, scale * 0.01f, scale * 3.0f );

	DXUTGetD3DDevice()->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

	D3DXVECTOR3	SceneMin(&g_scene.aabb.minV);
	D3DXVECTOR3	SceneMax(&g_scene.aabb.maxV);
	g_pShadowSolution->SetSceneMin(SceneMin);
	g_pShadowSolution->SetSceneMax(SceneMax);

	return true;
}

// load mesh from "OBJ" or "ASE" file
// creat the CRT scene
// convert mesh to d3d mesh format

bool OnLoadScene()
{
	// Initial and target filename
	TCHAR filename[256] = {0};
	GetModuleFileName((HMODULE)DXUTGetHINSTANCE(), filename, 256);
	TCHAR* escapechar = wcsrchr(filename, '\\');
	escapechar[1] = 0;
	wcscat_s(filename, L"Media\\scene.obj");

	// Let user choose which name template to use
	OPENFILENAME	ofn;
	ZeroMemory(&ofn, sizeof(OPENFILENAME));

	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner	= DXUTGetHWND();
	ofn.lpstrFilter = L"Scene file\0*.obj\0\0";
	ofn.nFilterIndex= 1;
	ofn.lpstrFile	= filename;
	ofn.nMaxFile	= 256;
	ofn.lpstrTitle	= L"Load scene...";
	ofn.Flags		= OFN_EXPLORER | OFN_FILEMUSTEXIST;

	if (GetOpenFileName(&ofn))
	{
		return LoadScene(filename);
	}
	else
		return false;
}

bool SaveImage(TCHAR *filename)
{
	TCHAR* dot = wcsrchr(filename, '.');
	if (dot)
		dot[0] = 0;

	TCHAR fn[256];

	// image rendered by DX
	wcscpy_s(fn, filename);
	wcscat_s(fn, L"_dx.png");

	HRESULT hr;
	IDirect3DSurface9* pBackBuffer;
	hr = DXUTGetD3DDevice()->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer );
	if (hr != S_OK)
		return false;
	hr = D3DXSaveSurfaceToFile( fn, D3DXIFF_PNG, pBackBuffer, NULL, NULL ) ;
	pBackBuffer->Release();
	if (hr != S_OK)
		return false;

	// depth texture
	wcscpy_s(fn, filename);
	wcscat_s(fn, L"_sm.png");
	if (g_pShadowSolution->GetTech() != ST_NO)
	{
		V(g_pShadowSolution->DumpDepthTexture(fn));
	}

	// image rendered by CRT
	wcscpy_s(fn, filename);
	wcscat_s(fn, L"_crt.bmp");

	return SaveCRTResult( fn );
}

bool OnSaveImage()
{
	// Initial and target filename
	TCHAR filename[256] = {0};
	GetModuleFileName((HMODULE)DXUTGetHINSTANCE(), filename, 256);
	TCHAR* escapechar = wcsrchr(filename, '\\');
	escapechar[1] = 0;
	wcscat_s(filename, L"image\\image");

	// Let user choose which name template to use
	OPENFILENAME	ofn;
	ZeroMemory(&ofn, sizeof(OPENFILENAME));

	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner	= DXUTGetHWND();
	ofn.lpstrFilter = L"Screen shot\0*.*\0\0";
	ofn.nFilterIndex= 1;
	ofn.lpstrFile	= filename;
	ofn.nMaxFile	= 256;
	ofn.lpstrTitle	= L"Save images...";
	ofn.Flags		= OFN_EXPLORER | OFN_OVERWRITEPROMPT;

	if (GetSaveFileName(&ofn))
	{
		return SaveImage(filename);
	}
	else
		return false;
}

//--------------------------------------------------------------------------------------
// Handles the GUI events
//--------------------------------------------------------------------------------------
void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext )
{
    CDXUTComboBox* pComboBox = NULL;
    CDXUTSlider* pSlider = NULL;
	TCHAR msg[128];

    switch( nControlID )
    {
        case IDC_TOGGLEFULLSCREEN: DXUTToggleFullScreen(); break;
        //case IDC_TOGGLEREF:        DXUTToggleREF(); break;
        case IDC_CHANGEDEVICE:     g_SettingsDlg.SetActive( !g_SettingsDlg.IsActive() ); break;
        case IDC_RESET:            ResetOptions(); break;

		case IDC_LOADSCENE : OnLoadScene(); break;
		case IDC_SAVEIMAGE : OnSaveImage(); break;
		case IDC_RAYTRACE : 
			if (!CRTInProgress)
				OnTrace(); 
			break;

		case IDC_SHADOW : 
			g_output.shadow = g_HUD.GetCheckBox( IDC_SHADOW )->GetChecked();
			break;

		case IDC_SKYLIGHT :
			{
				std::vector<TCRTLight>& lights = g_scene.lights;

				if (g_HUD.GetCheckBox( IDC_SKYLIGHT )->GetChecked())
				{
					for (std::vector<TCRTLight>::iterator it = lights.begin(); it != lights.end(); it++)
					{
						it->type = CRT_LT_SKY;
					}
				}
				else
				{
					for (std::vector<TCRTLight>::iterator it = lights.begin(); it != lights.end(); it++)
					{
						it->type = CRT_LT_DIRECTIONAL;
					}
				}
				break;
			}

		case IDC_SHADOWRES : 
			pComboBox = g_HUD.GetComboBox( IDC_SHADOWRES );
			g_pShadowSolution->SetResolution((ShadowRes)pComboBox->FindItem(pComboBox->GetSelectedItem()->strText));
			break;

		case IDC_SHADOWTECH :
			pComboBox = g_HUD.GetComboBox( IDC_SHADOWTECH );
			g_pShadowSolution->SetTech((ShadowTech)pComboBox->FindItem(pComboBox->GetSelectedItem()->strText));
			break;

		case IDC_SHADOWSAMPLE :
			pComboBox = g_HUD.GetComboBox( IDC_SHADOWSAMPLE );
			g_pShadowSolution->SetSample((ShadowSample)pComboBox->FindItem(pComboBox->GetSelectedItem()->strText));
			break;

		case IDC_DEPTHBIAS : 
			pSlider = g_HUD.GetSlider( IDC_DEPTHBIAS );
			g_pShadowSolution->SetDepthBias( pSlider->GetValue() / 10000.0f );
			_sntprintf( msg, 128, TEXT("DepthBias: %.3f"), g_pShadowSolution->GetDepthBias() ); msg[127] = 0;
			g_HUD.GetStatic( IDC_DEPTHBIAS_STATIC )->SetText( msg );
			break;

		case IDC_FAROVERNEAR : 
			pSlider = g_HUD.GetSlider( IDC_FAROVERNEAR );
			g_pShadowSolution->SetFarOverNear( pSlider->GetValue() / 100.0f );
			_sntprintf( msg, 128, TEXT("FarOverNear: %.3f"), g_pShadowSolution->GetFarOverNear() ); msg[127] = 0;
			g_HUD.GetStatic( IDC_FAROVERNEAR_STATIC )->SetText( msg );
			break;
    }
}

//--------------------------------------------------------------------------------------
// This callback function will be called immediately after the Direct3D device has been 
// reset, which will happen after a lost device scenario. This is the best location to 
// create D3DPOOL_DEFAULT resources since these resources need to be reloaded whenever 
// the device is lost. Resources created here should be released in the OnLostDevice 
// callback. 
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, 
							   const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
{
	HRESULT hr;

	V_RETURN( g_DialogResourceManager.OnResetDevice() );
	V_RETURN( g_SettingsDlg.OnResetDevice() );

	if( g_pFont )
		V_RETURN( g_pFont->OnResetDevice() );
	if( g_pEffect )
		V_RETURN( g_pEffect->OnResetDevice() );
	if( g_pTextSprite )
		V_RETURN( g_pTextSprite->OnResetDevice() );

	// Create a sprite to help batch calls when drawing many lines of text
	V_RETURN( D3DXCreateSprite( pd3dDevice, &g_pTextSprite ) );	

	// Setup the camera's projection parameters
	g_Aspect = pBackBufferSurfaceDesc->Width / (FLOAT)pBackBufferSurfaceDesc->Height;
	g_Camera.SetProjParams( D3DX_PI/4, g_Aspect, g_Camera.GetNearClip(), g_Camera.GetFarClip() );
	g_Camera.SetRotateButtons( true, false, false );

	g_HUD.SetLocation( pBackBufferSurfaceDesc->Width-170, 0 );
	g_HUD.SetSize( 170, 170 );

	g_LightControl.OnResetDevice(pBackBufferSurfaceDesc);

	if (g_pShadowSolution)
		g_pShadowSolution->OnResetDevice(pd3dDevice);

	if (g_pQuadVD == NULL)
	{
		V_RETURN( pd3dDevice->CreateVertexDeclaration( QuadVE, &g_pQuadVD ) );
	}

	return S_OK;
}

//--------------------------------------------------------------------------------------
// This callback function will be called immediately after the Direct3D device has 
// entered a lost state and before IDirect3DDevice9::Reset is called. Resources created
// in the OnResetDevice callback should be released here, which generally includes all 
// D3DPOOL_DEFAULT resources. See the "Lost Devices" section of the documentation for 
// information about lost devices.
//--------------------------------------------------------------------------------------
void CALLBACK OnLostDevice( void* pUserContext )
{
    g_DialogResourceManager.OnLostDevice();
    g_SettingsDlg.OnLostDevice();

	CDXUTDirectionWidget::StaticOnLostDevice();

	if( g_pFont )
        g_pFont->OnLostDevice();
    if( g_pEffect )
        g_pEffect->OnLostDevice();

	g_pShadowSolution->OnLostDevice();

    SAFE_RELEASE( g_pTextSprite );
	SAFE_RELEASE(g_pQuadVD);
}


//--------------------------------------------------------------------------------------
// This callback function will be called immediately after the Direct3D device has 
// been destroyed, which generally happens as a result of application termination or 
// windowed/full screen toggles. Resources created in the OnCreateDevice callback 
// should be released here, which generally includes all D3DPOOL_MANAGED resources. 
//--------------------------------------------------------------------------------------
void CALLBACK OnDestroyDevice( void* pUserContext )
{
	SAFE_RELEASE( g_pQuadVD );

	g_DialogResourceManager.OnDestroyDevice();
    g_SettingsDlg.OnDestroyDevice();
    SAFE_RELEASE(g_pEffect);
    SAFE_RELEASE(g_pFont);
	SAFE_RELEASE(g_pTextSprite);

	CDXUTDirectionWidget::StaticOnDestroyDevice();

	for (vector<TD3DMesh>::iterator it = g_Meshes.begin(); it != g_Meshes.end(); it++)
	{
		SAFE_RELEASE(it->mesh);
	}

	SAFE_DELETE( g_pShadowSolution );
}


void ConvertScene()
{
	for (vector<TD3DMesh>::iterator it = g_Meshes.begin(); it != g_Meshes.end(); it++)
	{
		SAFE_RELEASE( it->mesh );
	}
	g_Meshes.clear();

	for (vector<TCRTMesh*>::iterator it = g_scene.meshes.begin(); it != g_scene.meshes.end(); it++)
	{
		TD3DMesh dxmesh;
		ConvertDXMesh(DXUTGetD3DDevice(), *(*it), dxmesh);
		//dxmesh.shadowtech = g_pEffect->GetTechniqueByName( "GenShadow" );

		g_Meshes.push_back(dxmesh);
	}

	g_LightColor		= D3DXVECTOR3( &g_scene.lights[0].color );
	g_LightDirection	= D3DXVECTOR3( &g_scene.lights[0].direction );

	g_LightControl.SetLightDirection( -g_LightDirection );
	g_LightControl.SetRadius( g_scene.aabb.GetSize().Length() );

	g_pShadowSolution->SetLightDirection( g_LightDirection );
}

HRESULT ConvertDXMesh(IDirect3DDevice9* pd3dDevice, TCRTMesh& mesh, TD3DMesh& result)
{
	HRESULT hr;

    // Create the encapsulated mesh
    ID3DXMesh* pMesh = NULL;
	V_RETURN( D3DXCreateMesh( (DWORD)mesh.triangles.size(), (DWORD)mesh.vertices_f.size(), 
                              D3DXMESH_MANAGED | D3DXMESH_32BIT, VERTEX_DECL, 
                              pd3dDevice, &pMesh ) ); 
    
    // Copy the vertex data
    TCRTVertexFull* pVertex;
    V_RETURN( pMesh->LockVertexBuffer( 0, (void**) &pVertex ) );
	memcpy( pVertex, &mesh.vertices_f[0], mesh.vertices_f.size() * sizeof(TCRTVertexFull) );
    pMesh->UnlockVertexBuffer();
    
    // Copy the index data
    DWORD* pIndex;
    V_RETURN( pMesh->LockIndexBuffer( 0, (void**) &pIndex ) );
    memcpy( pIndex, &mesh.triangles[0], mesh.triangles.size() * sizeof(TCRTTriangle) );
    pMesh->UnlockIndexBuffer();
    
    // Copy the attribute data
    DWORD* pSubset;
	DWORD subset = 0;
    V_RETURN( pMesh->LockAttributeBuffer( 0, &pSubset ) );
    memcpy( pSubset, &subset, sizeof(DWORD) );
    pMesh->UnlockAttributeBuffer();

    // Reorder the vertices according to subset and optimize the mesh for this graphics 
    // card's vertex cache. When rendering the mesh's triangle list the vertices will 
    // cache hit more often so it won't have to re-execute the vertex shader.
    DWORD* aAdjacency = new DWORD[mesh.triangles.size() * 3];
    if( aAdjacency == NULL )
        return E_OUTOFMEMORY;

    V( pMesh->GenerateAdjacency(1e-6f, aAdjacency) );
    V( pMesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE, aAdjacency, NULL, NULL, NULL) );
    
    SAFE_DELETE_ARRAY( aAdjacency );

	result.mesh		= pMesh;
	result.ambient	= D3DXVECTOR3( &mesh.material.ambient.GetVec3() );
	result.diffuse	= D3DXVECTOR3( &mesh.material.diffuse.GetVec3() );
	result.specular	= D3DXVECTOR3( &mesh.material.specular.GetVec3() );
	result.alpha	= 1.0f;
	result.shiness	= 15;
	//result.tech		= g_pEffect->GetTechniqueByName( "Specular" );

	return S_OK;
}

void ErrorMessageDialog(DWORD ErrorID)
{
    TCHAR	szBuf[80]; 
    LPVOID	lpMsgBuf;

	if (ErrorID)
	{
		FormatMessage(
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM,
			NULL,
			ErrorID,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
			(LPTSTR) &lpMsgBuf,
			0, NULL );

		wsprintf(szBuf, 
			L"Application failed with error %d: %s", 
			ErrorID, lpMsgBuf); 
	 
		MessageBox(NULL, szBuf, L"Error", MB_OK); 

		LocalFree(lpMsgBuf);
	}
}
